# ------ qt_app_ui_pur.py (优化后 - 不完全信息版本) ------
import sys
import os
import math
import time
import random
import numpy as np
from PyQt5.QtWidgets import (
    QApplication,
    QMainWindow,
    QWidget,
    QVBoxLayout,
    QHBoxLayout,
    QTableWidget,
    QTableWidgetItem,
    QLabel,
    QHeaderView,
    QSplitter,
    QPushButton,
    QStatusBar,
    QGroupBox,
    QGridLayout,
)
from PyQt5.QtGui import QPixmap, QImage, QPainter, QPen, QColor, QBrush, QFont, QIcon
from PyQt5.QtCore import Qt, QTimer, QPointF, QRectF


class GraphMapWidget(QWidget):
    def __init__(self, graph_manager=None, parent=None):
        super().__init__(parent)
        self.graph = graph_manager
        self.robots = []
        self.belief_pr = None
        self.belief_pos = None
        self.scale_factor = 1.0
        self.offset_x = 0
        self.offset_y = 0
        self.capture_count = 0
        # 定义角色颜色
        self.role_colors = {
            "evader": QColor(255, 0, 0),  # 红色 - 逃避者
            "pursuer": QColor(0, 0, 255),  # 蓝色 - 追捕者
        }
        self.setMinimumSize(800, 600)
        self.setMouseTracking(True)
        # 如果没有提供图管理器，创建模拟数据
        if self.graph is None:
            self.simulate_graph()

    def simulate_graph(self):
        """模拟图数据（在没有真实数据的情况下）"""
        self.graph = type("", (), {})()  # 创建空对象
        self.graph.node_coordinates = []
        self.graph.adj_matrix = []
        # 生成模拟节点
        num_nodes = 20
        for i in range(num_nodes):
            angle = 2 * math.pi * i / num_nodes
            radius = 200 + random.randint(-50, 50)
            x = 300 + radius * math.cos(angle)
            y = 300 + radius * math.sin(angle)
            self.graph.node_coordinates.append((x, y))
        # 创建模拟邻接矩阵
        self.graph.adj_matrix = [[0] * num_nodes for _ in range(num_nodes)]
        for i in range(num_nodes):
            for j in range(i + 1, num_nodes):
                if random.random() < 0.15:  # 15%的概率连接
                    self.graph.adj_matrix[i][j] = 1
                    self.graph.adj_matrix[j][i] = 1

    def update_robots(self, robots, capture_count=0, belief_pr=None, belief_pos=None):
        """更新机器人状态和信念"""
        self.robots = robots
        self.belief_pr = belief_pr
        self.belief_pos = belief_pos
        self.capture_count = capture_count
        self.update()

    def world_to_pixel(self, x, y):
        """将世界坐标转换为像素坐标"""
        # 如果没有真实坐标，直接返回
        if (
            not hasattr(self.graph, "node_coordinates")
            or not self.graph.node_coordinates
        ):
            return int(x), int(y)
        # 计算缩放和平移后的位置
        scaled_x = int(x * self.scale_factor + self.offset_x)
        scaled_y = int(y * self.scale_factor + self.offset_y)
        # 反转Y轴：将数学坐标系转换为屏幕坐标系
        flipped_y = self.height() - scaled_y
        return scaled_x, flipped_y

    def paintEvent(self, event):
        """绘制地图和机器人（不完全信息版本）"""
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        # 绘制背景
        painter.fillRect(self.rect(), QColor(240, 240, 240))
        # 如果没有图数据，显示提示
        if (
            not hasattr(self.graph, "node_coordinates")
            or not self.graph.node_coordinates
        ):
            painter.setPen(QPen(Qt.black))
            painter.setFont(QFont("Arial", 12))
            painter.drawText(self.rect(), Qt.AlignCenter, "没有地图数据可用")
            return
        node_num = len(self.graph.node_coordinates)
        # 绘制底图：细线边 + 空心圆圈节点
        node_radius = 4
        edge_width = 1
        for i in range(node_num):
            # 绘制节点：空心圆圈
            x, y = self.graph.node_coordinates[i]
            px, py = self.world_to_pixel(x, y)
            painter.setPen(QPen(QColor(80, 80, 80), 1))
            painter.setBrush(QBrush(Qt.NoBrush))  # 空心
            painter.drawEllipse(
                px - node_radius, py - node_radius, node_radius * 2, node_radius * 2
            )
            # 绘制边（细线）
            for j in range(i + 1, node_num):
                if (
                    hasattr(self.graph, "adj_matrix")
                    and i < len(self.graph.adj_matrix)
                    and j < len(self.graph.adj_matrix[i])
                ):
                    if self.graph.adj_matrix[i][j] == 1:
                        jx, jy = self.graph.node_coordinates[j]
                        pjx, pjy = self.world_to_pixel(jx, jy)
                        painter.setPen(QPen(QColor(180, 180, 180), edge_width))
                        painter.drawLine(px, py, pjx, pjy)
        # 绘制信念状态（追捕者视角下的逃逸者可能位置）
        if (
            self.belief_pr is not None
            and self.belief_pos is not None
            and len(self.belief_pr) == node_num
        ):
            max_pr = np.max(self.belief_pr)
            belief_base_radius = 4
            belief_scale = 8  # 最大半径 = base + scale
            for i in range(node_num):
                if self.belief_pos[i]:
                    x, y = self.graph.node_coordinates[i]
                    px, py = self.world_to_pixel(x, y)
                    pr = self.belief_pr[i]
                    # pr = math.log(pr) + 8
                    # pr = np.clip(pr, 0, 8) / 8

                    gray_intensity = int(200 - pr * 200)
                    belief_color = QColor(gray_intensity, 255, gray_intensity)
                    radius = belief_base_radius + int(pr * belief_scale)
                    painter.setPen(QPen(belief_color.darker(), 1))
                    painter.setBrush(QBrush(belief_color))
                    painter.drawEllipse(
                        px - radius, py - radius, radius * 2, radius * 2
                    )
                    # 绘制置信度标签（可选，小字体）
                    if pr >= 0.1:  # 只显示显著置信
                        painter.setPen(QPen(Qt.black))
                        painter.setFont(QFont("Arial", 6))
                        painter.drawText(px + radius + 2, py + 2, f"{pr:.1f}")
        # 绘制真实机器人位置（监控视角）
        for robot in self.robots:
            if (
                "position" not in robot
                or robot["position"] is None
                or "node" not in robot
                or robot["node"] is None
            ):
                continue
            try:
                # 使用节点坐标绘制（更精确）
                node_id = int(robot["node"])
                if 0 <= node_id < node_num:
                    x, y = self.graph.node_coordinates[node_id]
                else:
                    # 回退到解析字符串
                    pos_str = robot["position"].strip("()")
                    x, y = map(float, pos_str.split(","))
                px, py = self.world_to_pixel(x, y)
            except:
                continue
            # 根据角色选择颜色和样式
            role = robot.get("role", "unknown")
            color = self.role_colors.get(role, QColor(128, 128, 128))
            robot_radius = 10
            if role == "evader":
                # 逃逸者：红色圆圈（空心）
                painter.setPen(QPen(color, 2))
                painter.setBrush(QBrush(Qt.NoBrush))
            else:  # pursuer
                # 追捕者：蓝色实心圆
                painter.setPen(QPen(color.darker(), 2))
                painter.setBrush(QBrush(color.lighter(150)))
            painter.drawEllipse(
                px - robot_radius, py - robot_radius, robot_radius * 2, robot_radius * 2
            )
            # 绘制机器人ID和角色
            painter.setPen(QPen(Qt.black))
            painter.setFont(QFont("Arial", 9, QFont.Bold))
            role_symbol = "E" if role == "evader" else "P" if role == "pursuer" else "?"
            painter.drawText(px - 15, py - 15, f"{robot['id']}{role_symbol}")
            # 绘制目标位置（如果有）
            if (
                "target" in robot
                and robot["target"] != "未设置"
                and "target_node" in robot
                and robot["target_node"] != "未设置"
            ):
                try:
                    target_node_id = int(robot["target_node"])
                    if 0 <= target_node_id < node_num:
                        tx, ty = self.graph.node_coordinates[target_node_id]
                    else:
                        target_str = robot["target"].strip("()")
                        tx, ty = map(float, target_str.split(","))
                    tpx, tpy = self.world_to_pixel(tx, ty)
                    # 绘制目标线和点
                    painter.setPen(QPen(color, 2, Qt.DashLine))
                    painter.drawLine(px, py, tpx, tpy)
                    painter.setPen(QPen(color.darker(), 2))
                    painter.setBrush(QBrush(Qt.NoBrush))
                    target_point_radius = 5
                    painter.drawEllipse(
                        tpx - target_point_radius,
                        tpy - target_point_radius,
                        target_point_radius * 2,
                        target_point_radius * 2,
                    )
                except:
                    pass
            # 绘制状态指示器（外圈）
            status = robot.get("status", "unknown")
            if status == "active":
                status_color = Qt.green
            elif status == "connecting":
                status_color = Qt.yellow
            elif status == "disconnected":
                status_color = Qt.red
            else:
                status_color = Qt.gray
            painter.setPen(QPen(status_color, 2))
            painter.drawEllipse(
                px - robot_radius - 3,
                py - robot_radius - 3,
                (robot_radius + 3) * 2,
                (robot_radius + 3) * 2,
            )
        # 绘制标题和捕获次数
        painter.setPen(QPen(Qt.black))
        painter.setFont(QFont("Arial", 14, QFont.Bold))
        painter.drawText(20, 30, "不完全信息追捕游戏 - 实时监控")
        painter.setFont(QFont("Arial", 12))
        painter.drawText(20, 60, f"捕获次数: {self.capture_count}")

    def resizeEvent(self, event):
        """窗口大小改变时重新计算缩放因子"""
        super().resizeEvent(event)
        # 如果没有节点数据，不需要计算
        if (
            not hasattr(self.graph, "node_coordinates")
            or not self.graph.node_coordinates
        ):
            return
        # 找到节点的边界
        min_x = min(x for x, y in self.graph.node_coordinates)
        max_x = max(x for x, y in self.graph.node_coordinates)
        min_y = min(y for x, y in self.graph.node_coordinates)
        max_y = max(y for x, y in self.graph.node_coordinates)
        # 计算缩放因子
        width_ratio = (self.width() - 100) / (max_x - min_x) if max_x - min_x > 0 else 1
        height_ratio = (
            (self.height() - 100) / (max_y - min_y) if max_y - min_y > 0 else 1
        )
        self.scale_factor = min(width_ratio, height_ratio)
        # 计算偏移量以使图形居中
        self.offset_x = (
            self.width() - (max_x - min_x) * self.scale_factor
        ) / 2 - min_x * self.scale_factor
        self.offset_y = (
            self.height() - (max_y - min_y) * self.scale_factor
        ) / 2 - min_y * self.scale_factor


class RobotInfoTable(QTableWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        # 修改列数和表头（添加信念相关列）
        self.setColumnCount(6)
        self.setHorizontalHeaderLabels(
            ["Robot Name", "当前位置", "目标位置", "最后更新", "离线状态", "信念影响"]
        )

        # 设置表格样式
        self.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
        self.verticalHeader().setVisible(False)
        self.setSelectionBehavior(QTableWidget.SelectRows)
        self.setEditTriggers(QTableWidget.NoEditTriggers)

        # 设置列宽
        self.setColumnWidth(0, 100)  # Robot Name
        self.setColumnWidth(1, 150)  # 当前位置
        self.setColumnWidth(2, 150)  # 目标位置
        self.setColumnWidth(3, 100)  # 最后更新
        self.setColumnWidth(4, 100)  # 离线状态
        self.setColumnWidth(5, 120)  # 信念影响（追捕者专有）

        self.setStyleSheet(
            """
            QTableWidget {
                font-size: 11px;
                gridline-color: #e0e0e0;
            }
            QHeaderView::section {
                background-color: #f0f0f0;
                padding: 4px;
                border: 1px solid #d0d0d0;
                font-weight: bold;
            }
        """
        )

    def update_robots(self, robots):
        """更新机器人信息表格 (根据新要求优化)"""
        self.setRowCount(len(robots))
        for row, robot in enumerate(robots):
            robot_name = robot.get("name", f"Robot {row+1}")
            name_item = QTableWidgetItem(robot_name)
            name_item.setTextAlignment(Qt.AlignLeft | Qt.AlignVCenter)
            self.setItem(row, 0, name_item)

            # 当前位置
            pos_item = QTableWidgetItem(robot.get("position", "未知"))
            self.setItem(row, 1, pos_item)

            # 目标位置
            target_item = QTableWidgetItem(robot.get("target", "未设置"))
            self.setItem(row, 2, target_item)

            # 最后更新时间 (格式化为秒数)
            last_update_secs = robot.get("last_update", 0)
            last_update_str = f"{last_update_secs:.1f}秒前"
            update_item = QTableWidgetItem(last_update_str)
            update_item.setTextAlignment(Qt.AlignCenter)
            # 根据更新时间设置颜色 (可选)
            if last_update_secs > 10:
                update_item.setForeground(QColor(200, 0, 0))  # 红色
            elif last_update_secs > 5:
                update_item.setForeground(QColor(200, 100, 0))  # 橙色
            self.setItem(row, 3, update_item)

            # 离线状态 (基于 'status' 字段)
            status = robot.get("status", "未知")
            offline_status = "在线" if status == "active" else "离线"
            offline_item = QTableWidgetItem(offline_status)
            offline_item.setTextAlignment(Qt.AlignCenter)
            # 根据状态设置颜色
            if status == "active":
                offline_item.setForeground(QColor(0, 128, 0))  # 绿色
            else:  # connecting, disconnected, unknown
                offline_item.setForeground(QColor(200, 0, 0))  # 红色
            self.setItem(row, 4, offline_item)

            # 信念影响（仅追捕者：支持位置数或熵，逃逸者：N/A）
            if robot.get("role") == "pursuer":
                # 假设从额外数据获取，这里简化（实际从server.game_strategy）
                belief_impact = "支持: 5 | 熵: 2.1"  # 示例
                belief_item = QTableWidgetItem(belief_impact)
            else:
                belief_item = QTableWidgetItem("N/A")
            belief_item.setTextAlignment(Qt.AlignCenter)
            self.setItem(row, 5, belief_item)

            # 设置行背景色 (可选，根据状态区分)
            if status == "disconnected":
                for col in range(6):  # 修改为6列
                    item = self.item(row, col)
                    if item:
                        item.setBackground(QColor(245, 245, 245))  # 浅灰色


# --- 简化或移除 ControlPanel，因为游戏逻辑由服务器控制 ---
class ControlPanel(QGroupBox):
    def __init__(self, parent=None):
        super().__init__("游戏状态 & 信念摘要", parent)
        self.setStyleSheet(
            """
            QGroupBox {
                font-weight: bold;
                border: 1px solid gray;
                border-radius: 5px;
                margin-top: 0.5em;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 3px 0 3px;
            }
        """
        )
        layout = QVBoxLayout()  # 使用垂直布局
        # 游戏状态标签
        self.status_label = QLabel("游戏状态: 等待连接")
        self.status_label.setStyleSheet("font-size: 12px; font-weight: bold;")
        layout.addWidget(self.status_label)
        # 捕获次数标签
        self.capture_label = QLabel("捕获次数: 0")
        self.capture_label.setStyleSheet("font-size: 12px;")
        layout.addWidget(self.capture_label)
        # 信念摘要标签
        self.belief_label = QLabel("信念: 支持位置 0 | 熵 0.00")
        self.belief_label.setStyleSheet("font-size: 12px; color: #666;")
        layout.addWidget(self.belief_label)

        self.setLayout(layout)

    def update_status(
        self, game_active, capture_count, belief_pos=None, belief_pr=None
    ):
        """更新控制面板状态（添加信念摘要）"""
        self.capture_label.setText(f"捕获次数: {capture_count}")
        # 计算信念摘要
        if belief_pos is not None and belief_pr is not None:
            support_count = np.sum(belief_pos)
            if support_count > 0:
                entropy = -np.sum(
                    belief_pr[belief_pos] * np.log2(belief_pr[belief_pos] + 1e-10)
                )
            else:
                entropy = 0.0
            self.belief_label.setText(
                f"信念: 支持位置 {support_count} | 熵 {entropy:.2f}"
            )
        else:
            self.belief_label.setText("信念: 未初始化")
        # 游戏状态
        if game_active:
            self.status_label.setText("游戏状态: <font color='green'>进行中</font>")
        else:
            self.status_label.setText("游戏状态: <font color='orange'>暂停</font>")


class PursuitGameUI(QMainWindow):
    def __init__(self, server=None, logger=None):
        super().__init__()
        self.server = server
        self.setWindowTitle("不完全信息追捕游戏监控系统")
        self.setGeometry(100, 100, 1200, 800)
        self.logger = logger
        # 创建主布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        # 创建顶部状态栏
        self.status_bar = self.statusBar()
        self.status_label = QLabel("就绪")
        self.status_bar.addWidget(self.status_label)
        # 创建主分割器
        main_splitter = QSplitter(Qt.Horizontal)
        # 左侧：地图和控制面板
        left_widget = QWidget()
        left_layout = QVBoxLayout(left_widget)
        # 地图组件
        graph = server.graph if server else None
        self.map_widget = GraphMapWidget(graph)
        left_layout.addWidget(self.map_widget, 3)  # 3/4的空间
        # 控制面板
        self.control_panel = ControlPanel()
        left_layout.addWidget(self.control_panel, 1)  # 1/4的空间
        # 右侧：机器人信息表格
        self.robot_table = RobotInfoTable()
        # 添加到分割器
        main_splitter.addWidget(left_widget)
        main_splitter.addWidget(self.robot_table)
        main_splitter.setSizes([800, 400])  # 初始大小分配
        main_layout.addWidget(main_splitter)
        # 更新定时器
        self.update_timer = QTimer(self)
        self.update_timer.timeout.connect(self.update_display)
        self.update_timer.start(200)

    def update_display(self):
        """更新UI显示（添加信念数据）"""
        try:
            # 从服务器获取机器人信息和游戏状态
            robots = []
            game_active = False
            capture_count = 0
            belief_pr = None
            belief_pos = None

            if self.server:
                # 从服务器获取所有机器人信息
                robots = self.server.get_all_robots_info()  # 使用服务器提供的方法
                # 获取游戏状态
                game_active = self.server.game_active
                capture_count = self.server.game_strategy.capture_count
                # 获取信念数据
                belief_pr = self.server.game_strategy.belief_pr
                belief_pos = self.server.game_strategy.belief_pos
            else:
                # 模拟数据更新 (如果需要)
                # self.update_simulated_robots()
                # robots = self.sim_robots
                # game_active = True
                # capture_count = self.sim_capture_count
                pass  # 如果没有服务器且不模拟，保持空列表

            # 更新地图（传入信念）
            self.map_widget.update_robots(robots, capture_count, belief_pr, belief_pos)
            # 更新表格 (使用优化后的表格)
            self.robot_table.update_robots(robots)
            # 更新控制面板（传入信念）
            self.control_panel.update_status(
                game_active, capture_count, belief_pos, belief_pr
            )
            # 更新状态栏
            online_count = sum(1 for r in robots if r["status"] == "active")
            self.status_label.setText(
                f"机器人总数: {len(robots)} | "
                f"在线: {online_count} | "
                f"上次更新: {time.strftime('%H:%M:%S')}"
            )
        except Exception as e:
            if self.logger:
                self.logger.error(f"更新显示错误: {e}")
            else:
                print(f"更新显示错误: {e}")
